﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using DPLParser.DPLILCode;
using DPLParser.DPLNodeParser;
using DPLParser.DPLNodeParser.DPLScriptTreeNodeMappers.Implementations;
using Irony.Parsing;
using System.Linq;

namespace DPLParser
{
    public class DPLParser
    {
        Grammar _grammar;
        Parser _parser;
        ParseTree _parseTree;
        LanguageData _language;

        private List<string> _ParserErrors;
        public List<string> ParserErrors
        {
            get
            {
                if (_ParserErrors == null)
                    _ParserErrors = new List<string>();
                return _ParserErrors;
            }
            set { _ParserErrors = value; }
        }

        public DPLParser()
        {
            _grammar = new DPLGrammar();
            _language = new LanguageData(_grammar);
            _parser = new Parser(_language);
        }

        private List<DPLModule> _ParsedModules;
        public List<DPLModule> ParsedModules
        {
            get
            {
                if (_ParsedModules == null)
                    _ParsedModules = new List<DPLModule>();
                return _ParsedModules;
            }
        }

        private void FillParserErrors()
        {
            foreach (var aParserMessage in _parseTree.ParserMessages)
            {
                string strMessage = aParserMessage.Message;
                int line = aParserMessage.Location.Line;
                int column = aParserMessage.Location.Column;

                string strParserMessage = strMessage + " at line " + line + " column " + column;
                this.ParserErrors.Add(strParserMessage);
            }
        }

        public bool Parse(string strScript)
        {
            _parser.Parse(strScript);
            _parseTree = _parser.Context.CurrentParseTree;

            if (_parseTree.HasErrors())
            {
                FillParserErrors();
                return false;
            }

            DPLModule parsedModule = GetParsedModule();
            ParsedModules.Add(parsedModule);
            return true;
        }

        private DPLModule GetParsedModule()
        {
            DPLModule parsedModule = new DPLModule();
            parsedModule.ModuleName = ParseTreeNodeParsingExtensions.GetNodesContent(_parseTree.Root.GetNodes("compilation_unit.ModuleName"))[0];
            parsedModule.DPLScripts = new List<DPLScript>();

            ParseTreeNodeList patternDeclarations = _parseTree.Root.GetNodes("compilation_unit..PatternDeclaration");

            foreach (ParseTreeNode aParseTreeNode in patternDeclarations)
            {
                DPLScript aDPLScript = new DPLScript();
                aDPLScript.PatternName = ParseTreeNodeParsingExtensions.GetNodesContent(aParseTreeNode.GetNodes(".PatternName"))[0];
                aDPLScript.PatternVariables = new List<DPLVariable>();
                aDPLScript.ReducedInstructions = new List<DPLComparisonInstruction>();

                #region PARSE SCRIPT VARIABLES
                List<string> FactoredVariables = ParseTreeNodeParsingExtensions.GetNodesContent(aParseTreeNode.GetNodes(".PlainVariableFactorial.VariableDefinition"));

                List<string> ScriptOutputVariables = ParseTreeNodeParsingExtensions.GetNodesContent(aParseTreeNode.GetNodes(".PlainVariablesList.VariableDefinition"));

                var PlainVariableNodes = aParseTreeNode.GetNodes("PatternDeclaration..Operation..VariableDefinition");

                var allVariables = PlainVariableNodes.GetNodes("*.VariableDefinition");

                List<string> UniqueVariablesDefinition =
                    ParseTreeNodeParsingExtensions.GetNodesContent(allVariables).Distinct().ToList();

                foreach (string strVariable in UniqueVariablesDefinition)
                {
                    DPLVariable newDPLVariable = new DPLVariable();
                    newDPLVariable.VariableName = strVariable;
                    if (ScriptOutputVariables.Contains(newDPLVariable.VariableName))
                        newDPLVariable.IsVariablePublic = true;
                    if (FactoredVariables.Contains(newDPLVariable.VariableName))
                        newDPLVariable.IsFactoredOut = true;
                    aDPLScript.PatternVariables.Add(newDPLVariable);
                }
                #endregion

                ParseTreeNodeList OperationsNodes = aParseTreeNode.GetNodesWithDebugging("PatternDeclaration..Operation");
                foreach (ParseTreeNode anOperationNode in OperationsNodes)
                {
                    DPLReducedInstructionsExtractor instructionsExtractor = new DPLReducedInstructionsExtractor(anOperationNode);
                    aDPLScript.ReducedInstructions.AddRange(instructionsExtractor.GetList());
                }
                parsedModule.DPLScripts.Add(aDPLScript);
            }
            return parsedModule;
        }

    }
}
